package cn.sycoder.interceptor;

import cn.sycoder.entity.User;
import cn.sycoder.exception.InterceptorException;
import cn.sycoder.exception.SystemException;
import cn.sycoder.resp.Code;
import cn.sycoder.resp.RespCode;
import cn.sycoder.utils.JwtUtil;
import cn.sycoder.utils.UserUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Map;

/**
 * 登录拦截器
 */
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);

    // 存储 RedisTemplate 对象，用于与 Redis 数据库进行交互操作
    private final RedisTemplate<String, Object> redisTemplate;

    // 构造函数，依赖注入获取 RedisTemplate 对象
    @Autowired
    public LoginInterceptor(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        /**
         * 在请求到达Controller之前进行预处理，包括检查JWT的有效性、解析JWT以获取用户信息、以及根据用户信息执行权限控制等。
         * 通过拦截器，可以在不修改Controller代码的情况下，为整个应用提供统一的鉴权机制。
         */

        try {
//            // 模拟一个条件来抛出异常
//            boolean simulateError = true; // 您可以根据需要设置这个条件
//            if (simulateError) {
//                throw new InterceptorException(RespCode.LOGIN_FAIL_CODE,"模拟的拦截器异常");
//            }

            // 获取请求头中的token
            String token = request.getParameter("token");
            System.out.println("获取到的token：" + token);

            // 如果不是映射到方法则不进行拦截，直接放行
            if (!(handler instanceof HandlerMethod)) {
                return true;
            }

            // 判断token是否为空（有些操作不需要token，防止拦截）
            if (token == null) {
                return true;
            } else { // 如果token不为空，执行拦截验证

                String username = JwtUtil.extractUsername(token);
                System.out.println("当前登录用户：" + username);

                // 先从 Redis 中验证 token
                Map<String, Object> map = (Map<String, Object>) redisTemplate.opsForValue().get(token);
                String v1 = (String) map.get("loginUserName"); // 登录用户名
                Boolean v2 = (Boolean) map.get("isTokenExpired"); // 令牌是否过期 （false 未过期）
                if (username.equals(v1) && v2.equals(false)) { // redis 验证
                    // 验证成功-放行
                    System.out.println("========== redis ===========");
                    return true;
                }

                // 若 Redis 中未找到 token，再使用 JwtUtil 进行验证
                if (token == null || "".equals(token) || !JwtUtil.verifyToken(token, username)) { // 返回 true 登录成功，不执行该代码块
                    response.setCharacterEncoding("UTF-8");
                    response.setContentType("application/json;charset=utf-8");
                    try (PrintWriter writer = response.getWriter()) {
                        writer.print("登录校验失败");
                    } catch (Exception e) {
                        logger.error("login token error is {}", e.getMessage());
                    }
                    return false;
                }
                boolean ret = JwtUtil.verifyToken(token, username);
                if (ret) {
                    return true;
                }

                //若token验证成功，把用户信息存储在ThreadLocal
                Long userID = JwtUtil.extractUserID(token); // 获取token中的userID
                String userName = username; // 获取token中的userName
                User user = new User();
                user.setUserId(userID);
                user.setUserName(userName);
                UserUtils.setLoginUser(user);


                // 将 token 和用户信息存入Redis（以 token为 key，鉴权信息为 value）
                Map<String, Object> info = JwtUtil.authInfo(token);
                redisTemplate.opsForValue().set(token, info);
                return true;
            }

        } catch (InterceptorException e) {// 捕获异常
            throw new InterceptorException(Code.INTERCEPTOR, "系统繁忙，请稍后再试！");
        } catch (Exception e) {
            logger.error("在拦截器中发生了未捕获的异常", e);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务器内部错误");
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行了拦截器的postHandle方法");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 清除线程变量
        UserUtils.removeUser();
    }
}
